// Listing 12.11 Virtual copy constructor
#include <iostream>
#include <type_traits>
using namespace std;

class Mammal {
public:
  Mammal() : itsAge(1) { cout << "Mammal constructor...\n"; }
  virtual ~Mammal() { cout << "Mammal detructor...\n"; }
  Mammal(const Mammal &rhs);
  virtual void Speak() const { cout << "Mammal speak!\n"; }
  virtual Mammal *Clone() { return new Mammal(*this); }
  int GetAge() const { return itsAge; }

protected:
  int itsAge;
};

Mammal::Mammal(const Mammal &rhs) : itsAge(rhs.GetAge()) {
  cout << "Mammal Copy constructor...\n";
}

class Dog : public Mammal{
public:
  Dog() { cout << "Dog constructor...\n"; }
  virtual ~Dog() { cout << "Dog destructor...\n"; }
  Dog(const Dog &rhs);
  void Speak() const { cout << "Woof!\n!"; }
  virtual Mammal *Clone() { return new Dog(*this); }
};

Dog::Dog(const Dog &rhs) : Mammal(rhs) { cout << "Dog copy constructor...\n"; }

class Cat : public Mammal {
public:
  Cat() { cout << "Cat construtor...\n"; }
  ~Cat() { cout << "Cat destructor...\n"; }
  Cat(const Cat &);
  void Speak() const { cout << "Meow!\n"; }
  virtual Mammal *Clone() { return new Cat(*this); }
};

Cat::Cat(const Cat &rhs) : Mammal(rhs) { cout << "Cat copy constructor...\n"; }

enum ANIMALS { MAMMAL, DOG, CAT };
const int NumAnimalType = 3;
int main(void) {
  Mammal *theArray[NumAnimalType];
  Mammal *ptr;
  int choice, i;
  for (i = 0; i < NumAnimalType; i++) {
    cout << "(1)dog (2)cat (3)Mammal: "; 
    cin >> choice;
    switch (choice) {
      case DOG:
        ptr = new Dog;
        break;
      case CAT:
        ptr = new Cat;
        break;
      default:
        ptr = new Mammal;
        break; 
    }
    theArray[i] = ptr;
  }

  Mammal* OtherArray[NumAnimalType];
  for(i = 0; i < NumAnimalType; i++) {
    theArray[i]->Speak();
    OtherArray[i] = theArray[i]->Clone();
  }

  for(i = 0; i < NumAnimalType; i++) {
    OtherArray[i]->Speak();
  }
  return 0;
}
